package com.fyy.utils.http;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Objects;

/**
 * @Author fyy
 * @Description Http/Https请求工具类
 * @Date 2021/1/26 8:57
 */
public class HttpUtil {

    private static Logger logger = LoggerFactory.getLogger(HttpUtil.class);

    private HttpUtil(){}; //私有构造方法

    private static CloseableHttpClient httpClient; //多线程共享实例

    static {
        try {
            SSLContext sslContext = createSSLContext();
            SSLConnectionSocketFactory sslsf =
                    new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
            //注册套接字工厂
            Registry<ConnectionSocketFactory> socketFactoryRegistry =
                    RegistryBuilder.<ConnectionSocketFactory>create()
                            .register("http", PlainConnectionSocketFactory.INSTANCE)
                            .register("https",sslsf)
                            .build();
            //连接池管理器
            PoolingHttpClientConnectionManager connMgr =
                    new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            connMgr.setMaxTotal(300); //最大连接数
            connMgr.setDefaultMaxPerRoute(300); //每个路由最大连接数
            connMgr.setValidateAfterInactivity(5 *1000); //验证时常
            //请求参数配置管理器
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(60 * 1000) //连接主机创建连接的超时时间60s
                    .setSocketTimeout(120 * 1000) //读取超时时间120s
                    .setConnectionRequestTimeout(60 * 1000) //从链接池获取连接超时60s
                    .build();
            //获取客户端
            httpClient = HttpClients.custom()
                    .setConnectionManager(connMgr)
                    .setDefaultRequestConfig(requestConfig)
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @Author fyy
     * @Description 配置证书
     * @Date 2021/1/26 9:06
     */
    private static SSLContext createSSLContext() throws Exception{
        TrustStrategy strategy = (cert,authType) -> true; //信任所有
        SSLContext sslContext = SSLContexts.custom()
                .loadTrustMaterial(null,strategy)
                .build();
        return sslContext;
    }

    /**
     * @Author fyy
     * @Description 请求方式
     * @Date 2021/1/26 9:52
     */
    public enum HttpMethod{
        GET,POST,PUT,DELETE
    }

    /**
     * @Author fyy
     * @Description 请求结果封装
     * @Date 2021/1/26 9:54
     */
    public static class RequestResult{
        private boolean isSucc;
        private String msg;

        public boolean isSucc() {
            return isSucc;
        }

        public void setSucc(boolean succ) {
            isSucc = succ;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }
    }

    /**
     * @Author fyy
     * @Description 构造请求方法
     * @param method 请求方式
     * @param url 请求路径
     * @Date 2021/1/26 9:56
     */
    private static HttpRequestBase buildHttpMethod(HttpMethod method,String url){
        switch (method){
            case GET:
                return new HttpGet(url);
            case PUT:
                return new HttpPut(url);
            case POST:
                return new HttpPost(url);
            case DELETE:
                return new HttpDelete(url);
            default:
                return null;
        }
    }
    
    /**
     * @Author fyy
     * @Description 发送http请求
     * @param httpMethod 请求方式 GET\POST\PUT\DELETE
     * @param url 请求路径
     * @param header 请求头
     * @param params 请求主体(JSON数据)
     * @Date 2021/1/26 9:28
     */
    public static String sendHttp(HttpMethod httpMethod, String url, Map<String,String> header,String params) throws IOException {
        String infoMessage = new StringBuilder()
                .append("请求路径url:")
                .append(url)
                .append(",请求方式：")
                .append(httpMethod.name())
                .append(",请求头信息：")
                .append(JSONObject.toJSONString(header))
                .append(",请求参数：")
                .append(params).toString();
        logger.info("请求信息:{}",infoMessage);
        String result = null;
        long startTime = System.currentTimeMillis();
        ContentType contentType = ContentType.APPLICATION_JSON.withCharset(Charset.forName("UTF-8"));
        HttpRequestBase request = buildHttpMethod(httpMethod,url);
        if(Objects.nonNull(header) && !header.isEmpty()){ //头部信息非空
            //封装头部信息
            for (Map.Entry<String, String> entry : header.entrySet()) {
                request.setHeader(entry.getKey(),entry.getValue());
            }
            //封装参数
            if(!StringUtils.isEmpty(params)){ //参数非空
                if(HttpMethod.POST.equals(httpMethod) || HttpMethod.PUT.equals(httpMethod)){
                    ((HttpEntityEnclosingRequest)request).setEntity(new StringEntity(params,contentType));
                }
            }
            CloseableHttpResponse response = httpClient.execute(request);
            HttpEntity httpEntity = response.getEntity();
            logger.info("请求状态编码：{}",response.getStatusLine());
            if(Objects.nonNull(httpEntity)){ //判断请求结果是否为空
                result = EntityUtils.toString(httpEntity,"utf-8");
                logger.info("请求结果：{}",result);
            }
            HttpClientUtils.closeQuietly(response); //关闭返回对象
        }
        long endTime = System.currentTimeMillis();
        logger.info("本次请求耗时：{}",endTime - startTime);
        return result;
    }

   //********************************GET*************************//
    /**
     * @Author fyy
     * @Description GET请求
     * @param url 请求路径
     * @Date 2021/1/26 10:17
     */
    public static String getUrl(String url) throws IOException {
        return sendHttp(HttpMethod.GET,url,null,null);
    }

    /**
     * @Author fyy
     * @Description GET请求
     * @param url 请求路径
     * @param header 请求头
     * @Date 2021/1/26 10:17
     */
    public static String getUrl(String url,Map<String,String> header) throws IOException {
        return sendHttp(HttpMethod.GET,url,header,null);
    }

    //********************************POST*************************//
    /**
     * @Author fyy
     * @Description POST请求
     * @param url 请求路径
     * @param header 请求头
     * @param params 请求参数
     * @Date 2021/1/26 10:22
     */
    public static String postJson(String url,Map<String,String> header,String params) throws IOException {
        return sendHttp(HttpMethod.POST,url,header,params);
    }

    /**
     * @Author fyy
     * @Description POST请求
     * @param url 请求路径
     * @param header 请求头
     * @Date 2021/1/26 10:22
     */
    public static String postJson(String url,Map<String,String> header) throws IOException {
        return postJson(url,header,null);
    }

    /**
     * @Author fyy
     * @Description POST请求
     * @param url 请求路径
     * @Date 2021/1/26 10:22
     */
    public static String postJson(String url) throws IOException {
        return postJson(url,null,null);
    }

    /**
     * @Author fyy
     * @Description POST请求
     * @param url 请求路径
     * @param params 请求参数
     * @Date 2021/1/26 10:22
     */
    public static String postJson(String url,String params) throws IOException {
        return postJson(url,null,params);
    }

    //********************************FILE UPLOAD/DOWN*************************//
    /**
     * @Author fyy
     * @Description 上传文件流
     * @param url 请求路径
     * @param header 请求头
     * @param params 请求参数
     * @param fileName 文件名称
     * @param in 文件输入流
     * @Date 2021/1/26 10:30
     */
    public static RequestResult postUploadFileStream(String url, Map<String,String> header
            , Map<String,String> params, String fileName, InputStream in) throws IOException {
        byte[] stream = inputStream2byte(in);
        return postUploadFileStream(url,header,params,fileName,stream);
    }

    /**
     * @Author fyy
     * @Description 文件上传
     * @param url 请求路径
     * @param header 请求头
     * @param params 请求参数
     * @param fileName 文件名称
     * @param stream 文件字节流
     * @Date 2021/1/26 10:45
     */
    public static RequestResult postUploadFileStream(String url, Map<String,String> header
            , Map<String,String> params, String fileName, byte[] stream) throws IOException {
        RequestResult result = new RequestResult();
        result.setSucc(false);
        if(StringUtils.isEmpty(fileName)){
            logger.error("上传文件名为空！");
            result.setMsg("上传文件名为空！");
            return result;
        }
        if(Objects.isNull(stream)){
            logger.error("上传文件流为空！");
            result.setMsg("上传文件流为空！");
            return result;
        }
        ContentType contentType = ContentType.MULTIPART_FORM_DATA.withCharset(Charset.forName("UTF-8"));
        HttpPost httpPost = new HttpPost(url);
        if (Objects.nonNull(header) && !header.isEmpty()) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                httpPost.setHeader(entry.getKey(), entry.getValue());
            }
        }
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.setCharset(Charset.forName("UTF-8"));//使用UTF-8
        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);//设置浏览器兼容模式
        if (Objects.nonNull(params) && !params.isEmpty()) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                builder.addPart(entry.getKey(), new StringBody(entry.getValue(), contentType));
            }
        }
        builder.addBinaryBody("file",  stream, contentType, fileName); //封装上传文件
        httpPost.setEntity(builder.build());
        CloseableHttpResponse response = httpClient.execute(httpPost);
        logger.info("请求状态编码：{}",response.getStatusLine());
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_NO_CONTENT) {
            result.setSucc(true);
        }
        HttpEntity httpEntity = response.getEntity();
        if (Objects.nonNull(httpEntity)) {
            String content = EntityUtils.toString(httpEntity, "UTF-8");
            logger.info("文件上传结果:{}", content);
            result.setMsg(content);
        }
        HttpClientUtils.closeQuietly(response);
        return result;
    }

    /**
     * @Author fyy
     * @Description 下载文件流
     * @param url 文件路径
     * @Date 2021/1/26 11:00
     */
    public static ByteArrayOutputStream getDownloadFileStream(String url) throws IOException {
        ByteArrayOutputStream bos = null;
        CloseableHttpResponse response = httpClient.execute(new HttpGet(url));
        logger.info("请求状态编码：{}",response.getStatusLine());
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == HttpStatus.SC_OK) {
            HttpEntity entity = response.getEntity();
            if (entity != null && entity.getContent() != null) {
                bos = copyInputStream(entity.getContent());
            }
        }
        HttpClientUtils.closeQuietly(response);
        return bos;
    }

    /**
     * @Author fyy
     * @Description 输入流转字节流
     * @param stream 输入流
     * @Date 2021/1/26 10:35
     */
    private static byte[] inputStream2byte(InputStream stream) throws IOException {
        ByteArrayOutputStream bos = null;
        bos = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        int n;
        while ((n = stream.read(b)) != -1){
            bos.write(b,0,n);
        }
        stream.close();
        bos.close();
        byte[] buffer = bos.toByteArray();
        return buffer;
    }

    /**
     * @Author fyy
     * @Description 复制文件流
     * @param stream 输入流
     * @Date 2021/1/26 10:40
     */
    private static ByteArrayOutputStream copyInputStream(InputStream stream) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = stream.read(buffer)) != -1){
            bos.write(buffer,0,len);
        }
        bos.flush();
        stream.close();
        bos.close();
        return bos;
    }

}
